package dnl.dtools.grm;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;

import org.apache.commons.io.FileUtils;

import de.schlichtherle.io.ArchiveDetector;
import de.schlichtherle.io.File;
import de.schlichtherle.io.FileInputStream;
import de.schlichtherle.io.FileOutputStream;

/**
 * Represents an entry on the local file system. Such an entry may be a regular
 * file, directory or an archived entry.
 * 
 * @author Daniel Or
 * 
 */
public class LocalFileSystemEntry extends Resource {

	private File file;

	static{
		File.setDefaultArchiveDetector(ArchiveDetector.ALL);
	}
	
	public LocalFileSystemEntry(URL url) {
		super(url);
		try {
			// URIs may not contain spaces. Thus, as a solution for local 
			// file systems we'll use the string based constructor.
			if(url.getProtocol().equals("file")){
				this.file = new File(url.getFile());
			}
			else {
				this.file = new File(url.toURI());
			}
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static LocalFileSystemEntry from(java.io.File file){ 
		try {
			return new LocalFileSystemEntry(file);
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}
	
	private LocalFileSystemEntry(java.io.File file) throws MalformedURLException {
		super(file.toURI().toURL());
		if (file instanceof File)
			this.file = (File) file;
		else
			this.file = new File(file);
	}

	public File getFile() {
		return file;
	}

	public void setFile(File file) {
		this.file = file;
	}

	@Override
	public boolean exists() {
		return this.file.exists();
	}
	
	@Override
	public String getName() {
		if(file.getName().length() != 0){
			return file.getName();
		}
		return file.getPath();
	}

	@Override
	public long getLastModified() {
		return file.lastModified();
	}

	@Override
	public boolean isDirectory() {
		return file.isDirectory() && !file.isArchive();
	}

	@Override
	public boolean isArchive() {
		return file.isArchive();
	}

	@Override
	public boolean isArchiveEntry() {
		return file.isEntry();
	}

	public boolean isDirectoryOrArchive() {
		return file.isArchive() || file.isDirectory();
	}

	@Override
	public boolean isHidden() {
		return file.isHidden();
	}

	@Override
	public boolean isLocal() {
		return true;
	}

	@Override
	public long getSize() {
		return file.getDelegate().length();
	}

	@Override
	public Resource[] list() throws ResourceExtractionException {
		if (!isDirectoryOrArchive())
			throw new UnsupportedOperationException(
					"The list() operation is available only for directories.");
		java.io.File[] files = file.listFiles();
		if(files == null){
			if(file.isArchive()){
				throw new ResourceExtractionException("The file '"+file+"' is not a valid archive!");
			}
			else {
				throw new ResourceExtractionException("Is '"+file+"' a valid directory or archive?");
			}
		}

		Resource[] resources = new Resource[files.length];
		for (int i = 0; i < resources.length; i++) {
			try {
				resources[i] = new LocalFileSystemEntry(files[i]);
			} catch (MalformedURLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return resources;
	}

	@Override
	public boolean delete() throws IOException {
		if (isDirectory()) {
			FileUtils.forceDelete(this.file);
			return true;
		}

		return file.deleteAll();
	}

	@Override
	public InputStream getInputStream() {
		if (!file.exists())
			return null;
		try {
			return new FileInputStream(file);
		} catch (FileNotFoundException e) {
			// The file exists. If we got here, something
			// very weird is going on.
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public OutputStream getOutputStream() {
		try {
			return new FileOutputStream(file);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public boolean renameTo(Resource dest) throws IOException {
		if (dest instanceof LocalFileSystemEntry) {
			LocalFileSystemEntry lfse = (LocalFileSystemEntry) dest;
			return file.renameTo(lfse.file);
		}
		return false;
	}

	@Override
	public Resource getParent() {
		java.io.File parent = file.getParentFile();
		if (parent == null) {
			return null;
		}
		try {
			return new LocalFileSystemEntry(parent);
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;

		}
	}

	@Override
	public Resource getChild(String name) {
		URL newurl = null;
		try {
			newurl = new URL(file.toURI().toURL().toString()+"/"+name);
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return new LocalFileSystemEntry(newurl);
	}

	@Override
	public boolean mkDir() throws IOException {
		return file.mkdir();
	}

	public boolean equals(Object other) {
		return file.equals(other);
	}

	public int hashCode() {
		return file.hashCode();
	}

	@Override
	public String toString() {
		return "'"+file.getPath()+"'";
	}

}
